<?php
/**
 * 日志类
 * 
 * Copyright (c) 2014~2019 https://desion.net All rights reserved.
 * Author: 阿林 QQ:120867629 <linhong00@gmail.com>
 */
declare (strict_types = 1);
namespace dsphp;

class Log
{
    // 日志信息
    protected $log = [];
    // 应用开始时间
    protected $beginTime;
    // 应用内存初始占用
    protected $beginMem;
    // 配置参数
    protected $config = [
        // 时间格式
        'time_format'    => 'c', // 2019-08-18T15:19:21+00:00
        // 最大文件大小
        'file_size'      => 2097152,
        // 日志保存目录(runtime路径内)
        'path'           => '',
        // 允许写入级别
        'allow'          => [],
        // 独立日志级别
        'apart_level'    => [],
        // 日志输出格式化
        'format'         => '[%s][%s] %s',
        // 是否实时写入
        'realtime_write' => false,
        // 
        'debug'          => true
    ];


    /**
     * 构造方法
     *
     * @param array  $config       配置
     */
    public function __construct(array $config = [])
    {
        $this->config = array_merge($this->config, $config);

        if (empty($this->config['format'])) {
            $this->config['format'] = '[%s][%s] %s';
        }

        $path = $this->config['path'] ?: 'log';
        $this->config['path'] = $config['runtime'] . $path;
        
        if (substr($this->config['path'], -1) != DIRECTORY_SEPARATOR) {
            $this->config['path'] .= DIRECTORY_SEPARATOR;
        }
    }

    /**
     * 设置初始信息
     *
     * @param bool $debug   调试状态
     * @param integer $men  初始内存
     * @param float $time   初始时间
     * @return void
     */
    public function init(bool $debug, int $men, float $time)
    {
        // 应用开始时间
        $this->beginTime = $time;
        // 应用内存初始占用
        $this->beginMem = $men;
        // 调试状态
        $this->config['debug'] = $debug;
    }

    /**
     * 清空日志
     */
    public function clear()
    {
        $this->log = [];
    }

    /**
     * 获取日志信息
     */
    public function getLog(): array
    {
        return $this->log;
    }

    /**
     * 保存日志信息
     *
     * @return bool
     */
    public function save(): bool
    {
        $info = [];
        $log  = $this->log;
        $day  = date('d');
        $path = $this->config['path'] . date('Ym') . DIRECTORY_SEPARATOR;
        !is_dir($path) && mkdir($path, 0755, true);

        foreach ($log as $level => $list) {
            $filename = "{$path}{$day}_{$level}.log";

            foreach ($list as $key => $val) {
                if (! is_string($val['message'])) {
                    $val['message'] = var_export($val['message'], true);
                }

                $time = date($this->config['time_format'], $val['time']);
                
                $list[$key] = sprintf($this->config['format'], $time, $val['type'], $val['message']);
            }

            if (empty($list)) {
                continue;
            }

            $this->_checkLogSize($filename);

            $list = implode(PHP_EOL, $list) . PHP_EOL;
            error_log($list, 3, $filename);
        }

        return true;
    }

    /**
     * 检查日志文件大小并自动生成备份文件
     *
     * @param string $destination 日志文件
     * @return void
     */
    private function _checkLogSize(string $destination): void
    {
        if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {
            try {
                rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination));
            } catch (\Exception $e) {
                //
            }
        }
    }

    /**
     * 记录日志信息
     *
     * @param  string $level   日志级别
     * @param  mixed  $message 日志信息
     * @param  array  $context 替换内容
     * @return void
     */
    public function log($level, $message, array $context = []): void
    {
        $this->record($message, $level, $context);
    }

    /**
     * 记录日志信息
     *
     * @param  mixed  $message 日志信息
     * @param  string $type    日志级别
     * @param  array  $context 替换内容
     * @param  bool   $lazy    默认，false 为立即写入，在开启实时写入时，均为false
     * @return $this
     */
    public function record($message, string $type = 'info', array $context = [], bool $lazy = true)
    {
        if (! $this->config['debug'] && $this->config['allow'] && ! in_array($type, $this->config['allow'])) {
            return $this;
        }

        if (is_string($message) && ! empty($context)) {
            $replace = [];
            foreach ($context as $key => $val) {
                $replace['{' . $key . '}'] = $val;
            }

            $message = strtr($message, $replace);
        }

        if (! empty($message) || 0 === $message) {
            $level = in_array($type, $this->config['apart_level']) ? $type : 'log';

            $this->log[$level][] = ['message' => $message, 'type' => $type, 'time' => time()];
        }

        if ($this->config['realtime_write'] || ! $lazy) {
            $this->save();
        }

        return $this;
    }

    /**
     * 记录警报信息
     *
     * @param mixed $message 日志信息
     * @param array $context 替换内容
     * @return void
     */
    public function alert($message, array $context = []): void
    {
        $this->log(__FUNCTION__, $message, $context);
    }

    /**
     * 记录错误信息
     *
     * @param mixed $message 日志信息
     * @param array $context 替换内容
     * @return void
     */
    public function error($message, array $context = []): void
    {
        $this->log(__FUNCTION__, $message, $context);
    }

    /**
     * 记录warning信息
     *
     * @param mixed $message 日志信息
     * @param array $context 替换内容
     * @return void
     */
    public function warning($message, array $context = []): void
    {
        $this->log(__FUNCTION__, $message, $context);
    }

    /**
     * 记录notice信息
     *
     * @param mixed $message 日志信息
     * @param array $context 替换内容
     * @return void
     */
    public function notice($message, array $context = []): void
    {
        $this->log(__FUNCTION__, $message, $context);
    }

    /**
     * 记录一般信息
     *
     * @param mixed $message 日志信息
     * @param array $context 替换内容
     * @return void
     */
    public function info($message, array $context = []): void
    {
        $this->log(__FUNCTION__, $message, $context);
    }

    /**
     * 记录调试信息
     *
     * @param mixed $message 日志信息
     * @param array $context 替换内容
     * @return void
     */
    public function debug($message, array $context = []): void
    {
        $this->log(__FUNCTION__, $message, $context);
    }

    /**
     * 记录sql信息
     *
     * @param mixed $message 日志信息
     * @param array $context 替换内容
     * @return void
     */
    public function sql($message, array $context = []): void
    {
        $this->log(__FUNCTION__, $message, $context);
    }

    // 其他自定义
    public function __call($method, $parameters)
    {
        $this->log($method, ...$parameters);
    }
}
